iT邦幫忙

2022 iThome 鐵人賽

DAY 21
0

今天真正要講的是overriding。但在進入主題之前,先做點熱身運動:


淺談Overloading

  • 物件導向程式設計領域有兩個文字相似而意義不同的術語:overloading和overriding。其中overriding就是本篇主題,下面會詳細說明。而overloading則另有所指。
  • Overloading意思是在同一namespace下,允許多個名稱相同而其signature(註1)不同的方法(函數)並存。'signature'又是另一個術語,指函數的名稱以及其參數的個數和型別,但不包括函數的傳回值(註2)。
  • Overloading常譯作「多載」或「重載」,筆者認為都未能充分達意。較為貼切的中譯,筆者認為是「同名異式」。較長但準確傳達了overloading的原義。
  • 以下是C++的overloading範例程式:
    #include <iostream>
    using namespace std;
    
    class Tree {
        public:
            string breed;
            Tree(string myBreed){   // constructor
                breed = myBreed;
            }
    
        // Overloading: 以下兩個blossom()函數名稱相同,但參數不同,即signature不同。
        // C++視作不同的函數。
        void blossom(string startDate, string endDate){
            cout << "The blooming season of peaches will start from " << startDate << " till " << endDate << " this year." << endl;
        }
    
        // signature是函數名稱加上參數(個數、型別),一般不包括傳回值。
        void blossom(string month){
            cout << this->breed << " blossoms in " << month << '.' << endl;
        }
    };
    
    
    int main(void) {
        Tree tree("KV Plum");
        tree.blossom("March 6", "April 28");  // 呼叫有兩個參數的blossom()。
        cout << endl;
        tree.blossom("May");           // 呼叫只有一個參數的blossom()。
        return 0;
    }
    
  • 輸出:
    https://ithelp.ithome.com.tw/upload/images/20221006/201484856ReKxPCq3V.png
  • 從上例可見,同一namespace下,類別內有兩個都叫作blossom()的member functions。這樣不但不會報錯,在呼叫時,C++的compiler還會依不同參數,自動找到正確的那個函數。
  • Overloading也算是polymorphism的一種,稱為compile-time polymorphism,或曰static or early binding。Polymorphism是物件導向的精華和「最終目的」,留待最後才說明。
  • 回到Python。基於當初的語言設計理念和特性,Python並無overloading機制。在同一namespace(借C++用語)下,只要函數名稱相同,就算signature有異,也會後者蓋掉前者。筆者看過有人用一些奇奇怪怪的方法模擬出Python的overloading,筆者覺得實用性不高,就不介紹了。

Overriding

  • Overriding在繼承的第一篇「起手式」中就有提及,不過未詳加說明。當時是考慮到第一篇是入門磚,不宜放太多材料。所以將繼承的overriding這個重要概念留到今天才正式介紹。
  • 定義:維基百科對overriding的解釋是

    Method overriding, in object-oriented programming, is a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes. It allows for a specific type of polymorphism (subtyping).

  • 用中文來說,就是在物件導向的繼承體系中,父類別原已提供某個方法(函數),子類別本可直接取用,但由於某些原因,子類別內再定義一個名稱相同(註3)的方法。當子類別呼叫該方法時,編譯器找尋該方法的程序是:先搜尋本類別,找到名稱相符者即執行之。找不到就往上一層在其父類別搜尋,找到執行,找不到則再往上找,一直找到最頂端的「炎黃二帝」為止(我們都是「炎黃子孫」嘛)。
  • 好有一比,要吃年夜飯老爸本來已準備好一個名為dine()的方法,內容為在家自己煮。孝順兒子則認為不好勞煩兩老,他特地也寫了一個dine()方法,內容則是到外面高檔餐廳享用。最後執行的是兒子的想法。這就是overriding。不知這比喻恰不恰當。
  • 講理論不如看code,還是沿用Tree類別:
    class Tree():
        def __init__(self, breed: str, age: int, height: int):   # constructor
            self.__breed = breed
            self.__age = age
            self.__height = height
    
        @property
        def breed(self) -> str:
            return self.__breed
    
        def show_info(self):
            print(f'{self.__breed=:10}{self.__age=:<10,}{self.__height=:<10,}')
    
        # Parent class has already defined a blossom() method.
        def blossom(self, start_date: str, end_date: str):
            print(f'Parent: The blooming season of peaches will start from {start_date} till {end_date} this year.')    
    
    
    class Hardwood(Tree):   # Inherited from Tree
        ...   # construtor略
    
        # Child's blossom() will replace(override) the parent's.
        def blossom(self, blossom_count: int):
            print(f'The estimated blossoms of this {self.breed} would be around {blossom_count:,} this season.')    
    
  • 印證程式:
    tree = Hardwood('peach', 1000, 20)
    tree.show_info()
    print()
    tree.blossom(1_500)
    
  • 輸出:
    https://ithelp.ithome.com.tw/upload/images/20221006/20148485csvnU8lMqp.png
  • tree.show_info()這行code,由於子類別Hardwood()並未定義自己的show_info()方法,所以Python的interpreter就往上找,看父類別有無同名方法。結果找到了,當然就執行囉。
  • 至於tree.blossom(1_500)這行,Python的interpreter一看:唔,我自己(Hardwood類別)就有blossom()方法啦(雖然父類別也有個blossom()),不必往上找,就是它了。最後呼叫的是子類別自己的blossom(),而不是父類別的。
  • 眼尖讀者一定看出,上面的code父子兩個blossom()方法的signature不一樣耶,會不會是這個原因,Python才「要仔唔要乸」(註4),選擇了子類別的方法?
  • 那麼我們就再來驗證囉:
    class Tree():
        def __init__(self, breed: str, age: int, height: int):   # constructor
            self.__breed = breed
            self.__age = age
            self.__height = height
    
        @property
        def breed(self) -> str:
            return self.__breed
    
        def show_info(self):
            print(f'{self.__breed=:10}{self.__age=:<10,}{self.__height=:<10,}')
    
        # Parent class has already defined a blossom() method.
        def blossom(self, start_date: str, end_date: str):
            print(f'Parent: The blooming season of peaches will start from {start_date} till {end_date} this year.')    
    
    class Hardwood(Tree):   # Inherited from Tree
        ...   # construtor略
    
        # Child's blossom() will replace(override) the parent's.
        def blossom(self, start_date: str, end_date: str):
            print(f'Child: The blooming season of peaches will start from {start_date} till {end_date} this year.')    
    
  • 這回子類別的blossom()方法修正成和父類別一樣,都接受兩個str型別的參數。父子兩個blossosm()'s的signature一致了:
    tree = Hardwood('peach', 1000, 20)
    tree.show_info()
    print()
    tree.blossom('March 6', 'April 28')vo
    
  • 輸出如下:
    https://ithelp.ithome.com.tw/upload/images/20221006/20148485xbBjkEijPq.png
  • 執行的依然是子類別的方法。這就非常清楚了:Python不管父子方法signature相不相同,只要同名即觸發overriding

Overriding總結

  • Overriding是繼承機制下的產物。對此產物的作用,到目前為止我們的理解是:overriding目的是子類別「修正」或「取代」父類別的同名方法。父子兩類別「內容不同」,但「意圖一致」。比如上例都是「開花」(blossom),只是「開」的方法有所不同。
  • 不過,overriding還有一個重要任務。還記得OO的終極目的是Polymorphism這句話嗎?Overriding機制其實就是為日後的多型Polymorphism鋪路。
  • 另外有一點必須說明:overriding和前面講的overloading在觸發時機上大大不同。Overriding是run-time時才決定的,所以也稱為dynamic or late binding。正因是run-time,才能實踐最後的Polymorphism。

註1: signature的中文有「簽章」、「簽名」、「簽名式」等多個譯名。筆者乾脆都用英文。

註2: 有關signature包不包括函數傳回值問題,一般是不包括,也有人認為包括或在某些情形下包括。而大部分C++的compilers都不允許同一namespace下有兩個名稱相同、參數數目型別相同,只有傳回值不同的函數。

註3: 在C++,如果父子的member functions雖同名但signature不同,呼叫的當然是子類別的function。這時根本不存在overriding問題。

註4: 粵俗,意即寧願留下兒子,趕走母親。


上一篇
子類別存取父類別的私有屬性和方法,真的不行嗎?
下一篇
John Doe有幾個「老豆」?
系列文
Oops! OOPP: An Introduction to Object-Oriented Programming in Python30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言